///--------------------------------------------------------------------
/// <file name="Xpcom.js">
///   An interop layer to facilitate communicating with NAMFox XPCOM objects.
/// </file>
///--------------------------------------------------------------------

NAMFox.ns(function() { with (NAMFox) {

this.$ = function(selector) {
    /// <summary>Retrieves the class object for the specified contract ID.</summary>
    /// <param name="selector">The contract ID for which to retrieve a class object OR an interface ID to retrieve the XPCOM interface for.</param>
    /// <returns type="ComponentClass" />

    if (selector) {
        if (selector[0] === "@") {
            return new ComponentClass(selector);
        }
        else {
            return Components.interfaces[selector];
        }
    }

    throw new Error("The selector is required for the $() function.");
}

function ComponentClass(contractId) {
    /// <summary>Creates a new ComponentClass instance which facilitates access to common XPCOM functions.</summary>
    /// <param name="contractId" type="string">The contract ID for which to retrieve a class object.</param>

    this._componentClass = Components.classes[contractId];

    if (!this._componentClass) {
        var console = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService);
        console.logStringMessage("The contract ID '" + (contractId || "NULL") + "' did not yield a known component class.");
    }
}

ComponentClass.prototype = {
    instance: function(interfaceId) {
        /// <summary>Creates an instance of the component given an interface nsID for that component to conform to.</summary>
        /// <param name="interfaceId" type="string">The ID of the interface that is returned to represent a class instance.</param>
        /// <returns>An instance of a particular class given the specified interface ID.</returns>

        return this._componentClass.createInstance(Components.interfaces[interfaceId]);
    },

    service: function(interfaceId) {
        /// <summary>Gets a reference to a particular XPCOM service based on the nsID of an interface for that service to conform to.</summary>
        /// <param name="interfaceId" type="string">The ID of the interface that is returned to represent a service.</param>
        /// <returns>A reference to a particular XPCOM service.</returns>
        
        return this._componentClass.getService(Components.interfaces[interfaceId]);
    }
};

$.createCallback = function(callback, context) {
    var args = [];
    
    for (var i = 2; i < arguments.length; ++i) {
        args.push(arguments[i]);
    }
    
    return function() {
        try {
            var args2 = [];
            for (var i = 0; i < arguments.length; ++i) {
                args2.push(arguments[i]);
            }
            
            args2 = args2.concat(args);
            callback.apply(context, args2);
        }
        catch (e) {
            $.error(e);
        }
    };
};

// Ajax
$.cacheHandling = {
    useCache: 0,
    bypassCache: 1
};

$.ajax = $("@namfox.neoseeker.com/ajax;1").service("nfxIAjax");

$.get = function(url, data, success, error, cacheHandling, cookie) {
    /// <summary>Gets data at a specific URL.</summary>
    /// <param name="url" type="String">
    ///   The URL against which the request will be made.
    /// </param>
    /// <param name="data" type="Object">
    ///   A object of data to send.
    /// </param>
    /// <param name="success" type="Function" optional="true">
    ///   (Optional) The success callback.
    /// </param>
    /// <param name="error" type="Function" optional="true">
    ///   (Optional) The error callback.
    /// </param>
    /// <param name="cacheHandling" type="Number" optional="true">
    ///   (Optional) Specifies how to handle Firefox's cache when making a request.
    /// </param>
    /// <param name="cookie" type="String" optional="true">
    ///   (Optional) A string of cookie data to send with the request.
    /// </param>
    /// <returns type="nsIXMLHttpRequest" />
    
    return $.ajax.get(url, $.wrapObjectLiteral(data), success, error, cacheHandling || $.cacheHandling.useCache, cookie || "");
};

$.post = function(url, data, success, error, cacheHandling, cookie) {
    /// <summary>Gets data at a specific URL.</summary>
    /// <param name="url" type="String">
    ///   The URL against which the request will be made.
    /// </param>
    /// <param name="data" type="Object">
    ///   A object of data to send.
    /// </param>
    /// <param name="success" type="Function" optional="true">
    ///   (Optional) The success callback.
    /// </param>
    /// <param name="error" type="Function" optional="true">
    ///   (Optional) The error callback.
    /// </param>
    /// <param name="cacheHandling" type="Number" optional="true">
    ///   (Optional) Specifies how to handle Firefox's cache when making a request.
    /// </param>
    /// <param name="cookie" type="String" optional="true">
    ///   (Optional) A string of cookie data to send with the request.
    /// </param>
    /// <returns type="nsIXMLHttpRequest" />
    
    return $.ajax.post(url, $.wrapObjectLiteral(data), success, error, cacheHandling || $.cacheHandling.useCache, cookie || "");
};

// Custom messages
$.messages = {
    _manager: $("@namfox.neoseeker.com/custom-messages/manager;1").service("nfxICustomMessageManager"),
    
    backup: function(filePath) {
        /// <summary>Backs up the custom messages to a location specified by the user.</summary>
        /// <param name="filePath" optional="true">(Optional) The file path where custom messages will be saved.</param>
        
        this._manager.backup(filePath);
    },
    
    enumerate: function(callback) {
        /// <summary>Enumerates all of the NAMFox custom messages, except for the default message.</summary>
        /// <param name="callback" type="Function">The callback function, invoked for each message.</param>
        
        var keyCount = {};
        var keys = this._manager.messages.getKeys(keyCount);
        
        for (var i = 0, length = keyCount.value; i < length; ++i) {
            try {
                callback(this.getMessage(keys[i]).QueryInterface($("nfxINamedCustomMessage")));
            }
            catch (e) {
                $.error(e);
            }
        }
    },
    
    getDefaultMessage: function() {
        /// <summary>Gets the default custom message.</summary>
        /// <returns type="nfxICustomMessage" />
        
        return this._manager.defaultMessage;
    },
    
    getMessage: function(key) {
        /// <summary>Gets the custom message at a particular index.</summary>
        /// <param name="key" type="String">The key for the message to retrieve.</param>
        /// <returns type="nfxINamedCustomMessage"/>
        
        var message = this._manager.messages.getValue(key);
        
        try {
            return message.QueryInterface($("nfxINamedCustomMessage"));
        }
        catch (e) {
            $.error(e);
            return null;
        }
    },
    
    refresh: function() {
        /// <summary>Reloads custom messages from the default custom message file.</summary>
        
        this._manager.refresh();
    },
    
    restore: function(filePath) {
        /// <summary>Backs up the custom messages to a location specified by the user.</summary>
        /// <param name="filePath">The file path from which custom messages will be restored.</param>
        
        this._manager.restore(filePath);
    },
    
    save: function() {
        /// <summary>Saves the most recent custom message data to disk.</summary>
        
        this._manager.save();
    },
    
    size: function() {
        /// <summary>Gets the number of custom messages available.</summary>
        /// <returns type="Number" integer="true" />
    
        var keyCount = {};
        this._manager.messages.getKeys(keyCount);
        
        return keyCount.value;
    }
};

// Member Services
var memberService = $("@namfox.neoseeker.com/member-service;1").service("nfxIMemberService");
$.getCurrentUser = memberService.getCurrentUser;
$.refreshCurrentUser = memberService.refreshCurrentUser;
$.updateCustomSmileys = memberService.updateCustomSmileys;

// Configuration
var configuration = $("@namfox.neoseeker.com/configuration;1").service("nfxIConfiguration");
$.getConfig = function() {
    /// <summary>Gets the NAMFox configuration object.</summary>
    /// <returns type="nfxIConfiguration" />
    
    return configuration;
}

this.LinkBehavior = {
    OpenInNewTabWithMiddleClick: 1,
    AlwaysOpenInNewTab: 2
};

this.StripOptions = {
    Color: 1,
    SizeOne: 2,
    Sub: 4,
    Sup: 8
};

// Data Cache
$.dataKeys = {
    quickReplyMessage: "QRM"
};

$.dataRepository = {
    _cache: $("@namfox.neoseeker.com/data-repository;1").service("nfxIDataRepository"),
    
    getData: function(key) {
        /// <summary>Gets arbitrary data from the data repository based on the key.</summary>
        /// <param name="key" type="String">The key of the data to retrieve. The DataKeys variable has well-known keys whose values already exist in the repository.</param>
        /// <returns type="Object" />
        
        var data = this._cache.getData(key);
        
        if (data) {
            return data.wrappedJSObject;
        }
        
        return null;
    },
    
    setData: function(key, data) {
        /// <summary>Sets arbitrary data in the data repository based on the key.</summary>
        /// <param name="key" type="String">The key of the data to set. The DataKeys variable has well-known keys whose values already exist in the repository.</param>
        /// <param name="data" type="Object">The data to set</param>
        
        this._cache.setData(key, (data === null || data === undefined) ? null : $.wrapObjectLiteral(data));
    }
};

// Tracing
$.trace = $("@namfox.neoseeker.com/trace;1").service("nfxITrace");
$.assert = $.trace.assert;
$.fail = $.trace.fail;
$.info = $.trace.info;
$.verbose = $.trace.verbose;
$.error = function (e) {
    $.trace.error(e, e.stack);
};
$.warn = function (e) {
    $.trace.warn(e, e.stack);
};

$.traceLevels = {};
$.traceLevels.Off = 0;
$.traceLevels.TraceError = 1;
$.traceLevels.TraceWarning = 2;
$.traceLevels.TraceInfo = 3;
$.traceLevels.TraceVerbose = 4;

// Text Transformers
$.transformers = {};
$.transformers.gamespot = $("@namfox.neoseeker.com/transformers/gamespot;1").service("nfxITextTransformer").transform;
$.transformers.gametrailers = $("@namfox.neoseeker.com/transformers/gametrailers;1").service("nfxITextTransformer").transform;
$.transformers.gametrailersUser = $("@namfox.neoseeker.com/transformers/gametrailers-user;1").service("nfxITextTransformer").transform;
$.transformers.gamevideos = $("@namfox.neoseeker.com/transformers/gamevideos;1").service("nfxITextTransformer").transform;

// Url Encoding
var encoder = $("@namfox.neoseeker.com/encoder;1").service("nfxIEncoder");
$.attrEncode = encoder.attrEncode;
$.urlEncode = encoder.urlEncode;

// URL Resolution
$.url = $("@namfox.neoseeker.com/urlResolver;1").service("nfxIUrlResolver");

$.dumpObject = function(o) {
    if (o === null || o === undefined) {
        $.verbose("Object passed to dump object is null or undefined.");
        return;
    }
    
    for (var i in o) {
        if (o.hasOwnProperty(i)) {
            $.verbose(i + " : " + o[i]);
        }
    }
};

$.fxVersion = {
    _comparer: $("@mozilla.org/xpcom/version-comparator;1").service("nsIVersionComparator"),
    _version: $("@mozilla.org/xre/app-info;1").service("nsIXULAppInfo").version,
    
    equals: function(version) {
        return this._comparer.compare(this._version, version) === 0;
    },
    
    lessThanOrEqualTo: function(version) {
        return this._comparer.compare(this._version, version) <= 0;
    },
    
    greaterThanOrEqualTo: function(version) {
        return this._comparer.compare(this._version, version) >= 0;
    }
};

$.wrapObjectLiteral = function(o) {
    /// <summary>Wraps an object literal to send to XPCOM.</summary>
    /// <param name="o" type="Object">The Object to wrap before pushing it to XPCOM.</param>
    /// <returns type="Object" />
    
    return {
        wrappedJSObject: o
    };
};

$.contains = function(str, substring) {
    /// <summary>Returns a value indicating whether the specified String object occurs within this string.</summary>
    /// <param name="str" type="String">The String object in which to search for the substring.</param>
    /// <param name="substring" type="String">The String object to seek.</param>
    /// <returns type="Boolean" />

    return str && str.indexOf(substring) !== -1;
};

$.toRegExp = function(str, flags) {
    /// <summary>Converts a string into a regular expression (RegExp) object.</summary>
    /// <param name="str" type="String">The String object to convert to a RegExp.</param>
    /// <param name="flags" optional="true">(Optional) Flags to pass to the RegExp constructor.</param>
    /// <returns type="RegExp" />

    return new RegExp(str.replace(/([\\\^\$*+[\]?{}.=!:(|)])/g, "\\$1"), flags || "");
};

$.repeat = function(original, count) {
    /// <summary>
    ///   Creates a string by repeating the given string
    ///   a certain number of times.
    /// </summary>
    /// <param name="original" type="String">
    ///   The original string to repeat a certain number of times.
    /// </param>
    /// <param name="count" type="Number" integer="true">
    ///   The number of times the original string will occur in the result string.
    /// </param>
    /// <returns type="String" />

    var retVal = [];
    for (var i = 0; i < count; ++i) {
        retVal.push(original);
    }
    return retVal.join("");
};

$.startsWith = function(str, substring) {
    /// <summary>Determines whether the beginning of this instance matches the specified string.</summary>
    /// <param name="str" type="String">The String object in which to search for the substring.</param>
    /// <param name="substring" type="String">The String object to seek.</param>
    /// <returns type="Boolean" />

    return str.indexOf(substring) === 0;
};

$.endsWith = function(str, substring) {
    /// <summary>Determines whether the end of this instance matches the specified string.</summary>
    /// <param name="str" type="String">The String object in which to search for the substring.</param>
    /// <param name="substring" type="String">The String object to seek.</param>
    /// <returns type="Boolean" />

    return str.indexOf(substring) === str.length - substring.length;
};

$.trim = function(str) {
    /// <summary>Removes all occurrences of white space characters from the beginning and end of this instance.</summary>
    /// <param name="str" type="String">The String object from which to trim whitespace.</param>
    /// <returns type="String" />

    return str.replace(/^\s+|\s+$/g, "");
};

$.padLeft = function(str, totalWidth, paddingChar) {
    /// <summary>
    /// Right-aligns the characters in this instance, padding on the left
    /// with a specified character for a specified total length.
    /// </summary>
    /// <param name="str" type="String">The String object to pad from the left.</param>
    /// <param name="totalWidth" type="Number">
    /// The number of characters in the resulting string, equal to the
    /// number of original characters plus any additional padding characters.
    /// </param>
    /// <param name="paddingChar" type="String">
    /// A padding character.
    /// </param>
    /// <returns type="String" />

    paddingChar = paddingChar || ' ';
    if (paddingChar.length !== 1) {
        paddingChar = paddingChar[0];
    }

    var padLength = totalWidth - str.length;
    if (padLength > 0) {
        return $.repeat(paddingChar, padLength) + str;
    }

    return str;
};

}});